Implemented remaining portions of GtkCellLayout iface
authorTristan Van Berkom <tristan.van.berkom@gmail.com>
Sun, 24 Oct 2010 06:44:48 +0000 (15:44 +0900)
committerTristan Van Berkom <tristan.van.berkom@gmail.com>
Sun, 24 Oct 2010 06:44:48 +0000 (15:44 +0900)
Now GtkCellArea provides a generic way of applying attributes
from a GtkTreeModel/GtkTreeIter, GtkCellArea bookkeeps a hashtable
of GtkCellLayoutDataFunc's and completely abstracts the applying
of data to cells... GtkCellArea implementations need only to bookkeep
the added renderers and attributes (probably we can abstract the
attribute bookkeeping in the base class as well).

Things starting to take a good and practical shape.

gtk/gtkcellarea.c
gtk/gtkcellarea.h
gtk/gtkcellareabox.c

index cb85f5a7a8560bcab0118a93903769d81cf2c1b4..e4611e364e0113c47db013616caf8500e2bacabd 100644 (file)
 #include "gtkcelllayout.h"
 #include "gtkcellarea.h"
 
+/* GObjectClass */
+static void      gtk_cell_area_dispose                             (GObject            *object);
+static void      gtk_cell_area_finalize                            (GObject            *object);
+
 /* GtkCellAreaClass */
 static void      gtk_cell_area_real_get_preferred_height_for_width (GtkCellArea        *area,
                                                                    GtkWidget          *widget,
@@ -36,7 +40,6 @@ static void      gtk_cell_area_real_get_preferred_width_for_height (GtkCellArea
                                                                    gint               *minimum_width,
                                                                    gint               *natural_width);
 
-
 /* GtkCellLayoutIface */
 static void      gtk_cell_area_cell_layout_init              (GtkCellLayoutIface    *iface);
 static void      gtk_cell_area_pack_default                  (GtkCellLayout         *cell_layout,
@@ -47,35 +50,83 @@ static void      gtk_cell_area_add_attribute                 (GtkCellLayout
                                                              GtkCellRenderer       *renderer,
                                                              const gchar           *attribute,
                                                              gint                   id);
+static void      gtk_cell_area_set_cell_data_func            (GtkCellLayout         *cell_layout,
+                                                             GtkCellRenderer       *cell,
+                                                             GtkCellLayoutDataFunc  func,
+                                                             gpointer               func_data,
+                                                             GDestroyNotify         destroy);
 static void      gtk_cell_area_clear_attributes              (GtkCellLayout         *cell_layout,
                                                              GtkCellRenderer       *renderer);
+static void      gtk_cell_area_reorder                       (GtkCellLayout         *cell_layout,
+                                                             GtkCellRenderer       *cell,
+                                                             gint                   position);
 static GList    *gtk_cell_area_get_cells                     (GtkCellLayout         *cell_layout);
 
+/* GtkCellLayoutDataFunc handling */
+typedef struct {
+  GtkCellLayoutDataFunc func;
+  gpointer              data;
+  GDestroyNotify        destroy;
+} CustomCellData;
+
+static CustomCellData *custom_cell_data_new  (GtkCellLayoutDataFunc  func,
+                                             gpointer               data,
+                                             GDestroyNotify         destroy);
+static void            custom_cell_data_free (CustomCellData        *custom);
+
+/* Struct to pass data while looping over 
+ * cell renderer attributes
+ */
+typedef struct {
+  GtkCellArea  *area;
+  GtkTreeModel *model;
+  GtkTreeIter  *iter;
+} AttributeData;
+
+struct _GtkCellAreaPrivate
+{
+  GHashTable *custom_cell_data;
+};
+
 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GtkCellArea, gtk_cell_area, G_TYPE_INITIALLY_UNOWNED,
                                  G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,
                                                         gtk_cell_area_cell_layout_init));
 
-
 static void
 gtk_cell_area_init (GtkCellArea *area)
 {
+  GtkCellAreaPrivate *priv;
 
+  area->priv = G_TYPE_INSTANCE_GET_PRIVATE (area,
+                                           GTK_TYPE_CELL_AREA,
+                                           GtkCellAreaPrivate);
+  priv = area->priv;
+
+  priv->custom_cell_data = g_hash_table_new_full (g_direct_hash, 
+                                                 g_direct_equal,
+                                                 NULL, 
+                                                 (GDestroyNotify)custom_cell_data_free);
 }
 
 static void 
 gtk_cell_area_class_init (GtkCellAreaClass *class)
 {
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+  
+  /* GObjectClass */
+  object_class->dispose  = gtk_cell_area_dispose;
+  object_class->finalize = gtk_cell_area_finalize;
+
   /* general */
-  class->add                = NULL;
-  class->remove             = NULL;
-  class->forall             = NULL;
-  class->event              = NULL;
-  class->render             = NULL;
+  class->add     = NULL;
+  class->remove  = NULL;
+  class->forall  = NULL;
+  class->event   = NULL;
+  class->render  = NULL;
 
   /* attributes */
   class->attribute_connect    = NULL;
   class->attribute_disconnect = NULL;
-  class->attribute_apply      = NULL;
   class->attribute_forall     = NULL;
 
   /* geometry */
@@ -84,21 +135,104 @@ gtk_cell_area_class_init (GtkCellAreaClass *class)
   class->get_preferred_height           = NULL;
   class->get_preferred_height_for_width = gtk_cell_area_real_get_preferred_height_for_width;
   class->get_preferred_width_for_height = gtk_cell_area_real_get_preferred_width_for_height;
+
+  g_type_class_add_private (object_class, sizeof (GtkCellAreaPrivate));
+}
+
+
+/*************************************************************
+ *                      GObjectClass                         *
+ *************************************************************/
+static void
+gtk_cell_area_finalize (GObject *object)
+{
+  GtkCellArea        *area   = GTK_CELL_AREA (object);
+  GtkCellAreaPrivate *priv   = area->priv;
+
+  /* All cell renderers should already be removed at this point,
+   * just kill our hash table here. 
+   */
+  g_hash_table_destroy (priv->custom_cell_data);
+
+  G_OBJECT_CLASS (gtk_cell_area_parent_class)->finalize (object);
+}
+
+
+static void
+gtk_cell_area_dispose (GObject *object)
+{
+  /* This removes every cell renderer that may be added to the GtkCellArea,
+   * subclasses should be breaking references to the GtkCellRenderers 
+   * at this point.
+   */
+  gtk_cell_layout_clear (GTK_CELL_LAYOUT (object));
+
+  G_OBJECT_CLASS (gtk_cell_area_parent_class)->dispose (object);
 }
 
 
+/*************************************************************
+ *                    GtkCellAreaClass                       *
+ *************************************************************/
+static void
+gtk_cell_area_real_get_preferred_height_for_width (GtkCellArea        *area,
+                                                  GtkWidget          *widget,
+                                                  gint                width,
+                                                  gint               *minimum_height,
+                                                  gint               *natural_height)
+{
+  /* If the area doesnt do height-for-width, fallback on base preferred height */
+  GTK_CELL_AREA_GET_CLASS (area)->get_preferred_width (area, widget, minimum_height, natural_height);
+}
+
+static void
+gtk_cell_area_real_get_preferred_width_for_height (GtkCellArea        *area,
+                                                  GtkWidget          *widget,
+                                                  gint                height,
+                                                  gint               *minimum_width,
+                                                  gint               *natural_width)
+{
+  /* If the area doesnt do width-for-height, fallback on base preferred width */
+  GTK_CELL_AREA_GET_CLASS (area)->get_preferred_width (area, widget, minimum_width, natural_width);
+}
+
 /*************************************************************
  *                   GtkCellLayoutIface                      *
  *************************************************************/
+static CustomCellData *
+custom_cell_data_new (GtkCellLayoutDataFunc  func,
+                     gpointer               data,
+                     GDestroyNotify         destroy)
+{
+  CustomCellData *custom = g_slice_new (CustomCellData);
+  
+  custom->func    = func;
+  custom->data    = data;
+  custom->destroy = destroy;
+
+  return custom;
+}
+
+static void
+custom_cell_data_free (CustomCellData *custom)
+{
+  if (custom->destroy)
+    custom->destroy (custom->data);
+
+  g_slice_free (CustomCellData, custom);
+}
+
 static void
 gtk_cell_area_cell_layout_init (GtkCellLayoutIface *iface)
 {
-  iface->pack_start       = gtk_cell_area_pack_default;
-  iface->pack_end         = gtk_cell_area_pack_default;
-  iface->clear            = gtk_cell_area_clear;
-  iface->add_attribute    = gtk_cell_area_add_attribute;
-  iface->clear_attributes = gtk_cell_area_clear_attributes;
-  iface->get_cells        = gtk_cell_area_get_cells;
+  iface->pack_start         = gtk_cell_area_pack_default;
+  iface->pack_end           = gtk_cell_area_pack_default;
+  iface->clear              = gtk_cell_area_clear;
+  iface->add_attribute      = gtk_cell_area_add_attribute;
+  iface->set_cell_data_func = gtk_cell_area_set_cell_data_func;
+  iface->clear_attributes   = gtk_cell_area_clear_attributes;
+  iface->reorder            = gtk_cell_area_reorder;
+  iface->get_cells          = gtk_cell_area_get_cells;
 }
 
 static void
@@ -125,7 +259,6 @@ gtk_cell_area_clear (GtkCellLayout *cell_layout)
   g_list_free (cells);
 }
 
-
 static void
 gtk_cell_area_add_attribute (GtkCellLayout         *cell_layout,
                             GtkCellRenderer       *renderer,
@@ -136,7 +269,6 @@ gtk_cell_area_add_attribute (GtkCellLayout         *cell_layout,
                                   renderer, attribute, id);
 }
 
-
 typedef struct {
   const gchar *attribute;
   gchar        id;
@@ -157,6 +289,27 @@ accum_attributes (GtkCellArea      *area,
   *accum = g_list_prepend (*accum, attrib);
 }
 
+static void
+gtk_cell_area_set_cell_data_func (GtkCellLayout         *cell_layout,
+                                 GtkCellRenderer       *cell,
+                                 GtkCellLayoutDataFunc  func,
+                                 gpointer               func_data,
+                                 GDestroyNotify         destroy)
+{
+  GtkCellArea        *area   = GTK_CELL_AREA (cell_layout);
+  GtkCellAreaPrivate *priv   = area->priv;
+  CustomCellData     *custom;
+
+  if (func)
+    {
+      custom = custom_cell_data_new (func, func_data, destroy);
+      g_hash_table_insert (priv->custom_cell_data, cell, custom);
+    }
+  else
+    g_hash_table_remove (priv->custom_cell_data, cell);
+}
+
+
 static void
 gtk_cell_area_clear_attributes (GtkCellLayout         *cell_layout,
                                GtkCellRenderer       *renderer)
@@ -182,6 +335,14 @@ gtk_cell_area_clear_attributes (GtkCellLayout         *cell_layout,
   g_list_free (attributes);
 }
 
+static void 
+gtk_cell_area_reorder (GtkCellLayout   *cell_layout,
+                      GtkCellRenderer *cell,
+                      gint             position)
+{
+  g_warning ("GtkCellLayout::reorder not implemented for `%s'", 
+            g_type_name (G_TYPE_FROM_INSTANCE (cell_layout)));
+}
 
 static void
 accum_cells (GtkCellRenderer *renderer,
@@ -203,31 +364,6 @@ gtk_cell_area_get_cells (GtkCellLayout *cell_layout)
 }
 
 
-/*************************************************************
- *                    GtkCellAreaClass                       *
- *************************************************************/
-static void
-gtk_cell_area_real_get_preferred_height_for_width (GtkCellArea        *area,
-                                                  GtkWidget          *widget,
-                                                  gint                width,
-                                                  gint               *minimum_height,
-                                                  gint               *natural_height)
-{
-  /* If the area doesnt do height-for-width, fallback on base preferred height */
-  GTK_CELL_AREA_GET_CLASS (area)->get_preferred_width (area, widget, minimum_height, natural_height);
-}
-
-static void
-gtk_cell_area_real_get_preferred_width_for_height (GtkCellArea        *area,
-                                                  GtkWidget          *widget,
-                                                  gint                height,
-                                                  gint               *minimum_width,
-                                                  gint               *natural_width)
-{
-  /* If the area doesnt do width-for-height, fallback on base preferred width */
-  GTK_CELL_AREA_GET_CLASS (area)->get_preferred_width (area, widget, minimum_width, natural_width);
-}
-
 /*************************************************************
  *                            API                            *
  *************************************************************/
@@ -262,6 +398,10 @@ gtk_cell_area_remove (GtkCellArea        *area,
 
   class = GTK_CELL_AREA_GET_CLASS (area);
 
+  /* Remove any custom cell data func we have for this renderer */
+  gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (area),
+                                     renderer, NULL, NULL, NULL);
+
   if (class->remove)
     class->remove (area, renderer);
   else
@@ -333,7 +473,6 @@ gtk_cell_area_render (GtkCellArea        *area,
               g_type_name (G_TYPE_FROM_INSTANCE (area)));
 }
 
-
 /* Attributes */ 
 void
 gtk_cell_area_attribute_connect (GtkCellArea        *area,
@@ -377,25 +516,6 @@ gtk_cell_area_attribute_disconnect (GtkCellArea        *area,
               g_type_name (G_TYPE_FROM_INSTANCE (area)));
 }
 
-void
-gtk_cell_area_attribute_apply (GtkCellArea        *area,
-                              gint                id,
-                              GValue             *value)
-{
-  GtkCellAreaClass *class;
-
-  g_return_if_fail (GTK_IS_CELL_AREA (area));
-  g_return_if_fail (G_IS_VALUE (value));
-
-  class = GTK_CELL_AREA_GET_CLASS (area);
-
-  if (class->attribute_apply)
-    class->attribute_apply (area, id, value);
-  else
-    g_warning ("GtkCellAreaClass::attribute_apply not implemented for `%s'", 
-              g_type_name (G_TYPE_FROM_INSTANCE (area)));
-}
-
 void
 gtk_cell_area_attribute_forall (GtkCellArea             *area,
                                GtkCellRenderer         *renderer,
@@ -509,3 +629,67 @@ gtk_cell_area_get_preferred_width_for_height (GtkCellArea        *area,
   class = GTK_CELL_AREA_GET_CLASS (area);
   class->get_preferred_width_for_height (area, widget, height, minimum_width, natural_width);
 }
+
+
+static void 
+apply_attributes (GtkCellRenderer  *renderer,
+                 const gchar      *attribute,
+                 gint              id,
+                 AttributeData    *data)
+{
+  GValue value = { 0, };
+
+  /* For each attribute of each renderer we apply the value
+   * from the model to the renderer here 
+   */
+  gtk_tree_model_get_value (data->model, data->iter, id, &value);
+  g_object_set_property (G_OBJECT (renderer), attribute, &value);
+  g_value_unset (&value);
+}
+
+static void 
+apply_render_attributes (GtkCellRenderer *renderer,
+                        AttributeData   *data)
+{
+  gtk_cell_area_attribute_forall (data->area, renderer, 
+                                 (GtkCellAttributeCallback)apply_attributes,
+                                 data);
+}
+
+static void
+apply_custom_cell_data (GtkCellRenderer *renderer,
+                       CustomCellData  *custom,
+                       AttributeData   *data)
+{
+  g_assert (custom->func);
+
+  /* For each renderer that has a GtkCellLayoutDataFunc set,
+   * go ahead and envoke it to apply the data from the model 
+   */
+  custom->func (GTK_CELL_LAYOUT (data->area), renderer,
+               data->model, data->iter, custom->data);
+}
+
+void
+gtk_cell_area_apply_attributes (GtkCellArea  *area,
+                               GtkTreeModel *tree_model,
+                               GtkTreeIter  *iter)
+{
+  GtkCellAreaPrivate *priv;
+  AttributeData       data;
+
+  g_return_if_fail (GTK_IS_CELL_AREA (area));
+  g_return_if_fail (GTK_IS_TREE_MODEL (tree_model));
+  g_return_if_fail (iter != NULL);
+
+  priv = area->priv;
+
+  /* For every cell renderer, for every attribute, apply the attribute */
+  data.area  = area;
+  data.model = tree_model;
+  data.iter  = iter;
+  gtk_cell_area_forall (area, (GtkCellCallback)apply_render_attributes, &data);
+
+  /* Now go over any custom cell data functions */
+  g_hash_table_foreach (priv->custom_cell_data, (GHFunc)apply_custom_cell_data, &data);
+}
index 5da27720d93452a64402532ce601e50c54d95c9e..073f76a2d1c16d28289c3e9c020e849a0ce4256b 100644 (file)
@@ -30,6 +30,7 @@
 
 #include <gtk/gtkcellrenderer.h>
 #include <gtk/gtkwidget.h>
+#include <gtk/gtktreemodel.h>
 
 G_BEGIN_DECLS
 
@@ -42,6 +43,7 @@ G_BEGIN_DECLS
 
 typedef struct _GtkCellArea              GtkCellArea;
 typedef struct _GtkCellAreaClass         GtkCellAreaClass;
+typedef struct _GtkCellAreaPrivate       GtkCellAreaPrivate;
 
 
 /**
@@ -58,7 +60,6 @@ typedef void    (*GtkCellCallback)     (GtkCellRenderer  *renderer,
 
 /**
  * GtkCellAttributeCallback:
- * @area: the #GtkCellArea containing @renderer
  * @renderer: the #GtkCellRenderer that has an attribute
  * @attribute: the property attributed to @id
  * @id: the identifier of this attributed value
@@ -68,8 +69,7 @@ typedef void    (*GtkCellCallback)     (GtkCellRenderer  *renderer,
  * attributes of the cell renderers in a #GtkCellArea, 
  * see gtk_cell_area_attribute_forall().
  */
-typedef void    (*GtkCellAttributeCallback)     (GtkCellArea      *area,
-                                                GtkCellRenderer  *renderer,
+typedef void    (*GtkCellAttributeCallback)     (GtkCellRenderer  *renderer,
                                                 const gchar      *attribute,
                                                 gint              id,
                                                 gpointer          data);
@@ -79,6 +79,7 @@ struct _GtkCellArea
 {
   GInitiallyUnowned parent_instance;
 
+  GtkCellAreaPrivate *priv;
 };
 
 struct _GtkCellAreaClass
@@ -113,9 +114,6 @@ struct _GtkCellAreaClass
                                                          GtkCellRenderer         *renderer,
                                                          const gchar             *attribute,
                                                          gint                     id);
-  void               (* attribute_apply)                 (GtkCellArea             *area,
-                                                         gint                     id,
-                                                         GValue                  *value);
   void               (* attribute_forall)                (GtkCellArea             *area,
                                                          GtkCellRenderer         *renderer,
                                                          GtkCellAttributeCallback callback,
@@ -182,9 +180,6 @@ void               gtk_cell_area_attribute_disconnect           (GtkCellArea
                                                                 GtkCellRenderer         *renderer,
                                                                 const gchar             *attribute,
                                                                 gint                     id);
-void               gtk_cell_area_attribute_apply                (GtkCellArea             *area,
-                                                                gint                     id,
-                                                                GValue                  *value);
 void               gtk_cell_area_attribute_forall               (GtkCellArea             *area,
                                                                 GtkCellRenderer         *renderer,
                                                                 GtkCellAttributeCallback callback,
@@ -212,6 +207,12 @@ void               gtk_cell_area_get_preferred_width_for_height (GtkCellArea
                                                                 gint               *natural_width);
 
 
+/* Following apis are not class virtual methods */
+void               gtk_cell_area_apply_attributes               (GtkCellArea        *area,
+                                                                GtkTreeModel       *tree_model,
+                                                                GtkTreeIter        *iter);
+
+
 G_END_DECLS
 
 #endif /* __GTK_CELL_AREA_H__ */
index 5c0beddcd02e50241c5ba9879b2d3d5141afc4b6..52db6e9ad174f1b4ecf8e9e96bb2fb488ab25fb6 100644 (file)
@@ -61,9 +61,6 @@ static void      gtk_cell_area_box_attribute_disconnect           (GtkCellArea
                                                                   GtkCellRenderer         *renderer,
                                                                   const gchar             *attribute,
                                                                   gint                     id);
-static void      gtk_cell_area_box_attribute_apply                (GtkCellArea             *area,
-                                                                  gint                     id,
-                                                                  GValue                  *value);
 static void      gtk_cell_area_box_attribute_forall               (GtkCellArea             *area,
                                                                   GtkCellRenderer         *renderer,
                                                                   GtkCellAttributeCallback callback,
@@ -141,7 +138,6 @@ gtk_cell_area_box_class_init (GtkCellAreaBoxClass *class)
   
   area_class->attribute_connect              = gtk_cell_area_box_attribute_connect;
   area_class->attribute_disconnect           = gtk_cell_area_box_attribute_disconnect;
-  area_class->attribute_apply                = gtk_cell_area_box_attribute_apply;
   area_class->attribute_forall               = gtk_cell_area_box_attribute_forall;
   
   area_class->get_request_mode               = gtk_cell_area_box_get_request_mode;
@@ -254,14 +250,6 @@ gtk_cell_area_box_attribute_disconnect (GtkCellArea             *area,
 
 }
 
-static void
-gtk_cell_area_box_attribute_apply (GtkCellArea             *area,
-                                  gint                     id,
-                                  GValue                  *value)
-{
-
-}
-
 static void
 gtk_cell_area_box_attribute_forall (GtkCellArea             *area,
                                    GtkCellRenderer         *renderer,